﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms.DataVisualization.Charting;
using System.Data;
using TSL_Tools.SqlTools;
using System.Data.SqlClient;
using System.IO;
using System.Xml;
using System.Diagnostics;
using System.ComponentModel;

namespace ReportingEngine
{
    public class TSL_Report_DataSets
    {
        #region -- Public Properties ----------------------------------------------------
        // All items here get graphs for the report
        public List<TSL_ChartingDataSingleChart> DataSetCollectionForCharting { get; set; }

        // All items here are table only items
        public List<TSL_ChartingDataSeries> DataSetCollectionForTables { get; set; }

        public List<System.Drawing.Color> plotColors { get; set; }

        public string errorString { get; private set; }

        #endregion

        #region -- Private Properties ---------------------------------------------------
        #endregion

        #region -- Constructors ---------------------------------------------------------
        public TSL_Report_DataSets() 
        {
            DataSetCollectionForCharting = new List<TSL_ChartingDataSingleChart>();
            DataSetCollectionForTables = new List<TSL_ChartingDataSeries>();
            plotColors = new List<System.Drawing.Color>();
        }
        #endregion

        #region -- Public Methods -------------------------------------------------------
        public TimeSpan ReadChartingObjectsConfigFile(string fileName, string machineAliasSet = "DefaultAliasSet")
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            try
            {
                Dictionary<string, string> aliases = new Dictionary<string, string>();

                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(fileName);

                #region -- PlotColors ------------------------------------------------------------------------
                //First build the plot colors list so it can be used going through the iterations
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("PlotColors"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        System.Drawing.Color c = (System.Drawing.Color)TypeDescriptor.GetConverter(typeof(System.Drawing.Color)).ConvertFromString(xChild.Attributes["color"].Value);
                        plotColors.Add(c);
                    }
                }
                #endregion

                #region -- Variable Names -------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName(machineAliasSet))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes != null)
                            aliases.Add(xChild.Attributes["name"].Value, xChild.Attributes["machineList"].Value);
                    }
                }
                #endregion

                #region -- Key Metrics -----------------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("KeyMetricsCounters"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes != null &&
                            xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            DataSetCollectionForCharting.Add(AddChart(xChild, "KeyMetricsCounters"));
                        }
                    }
                }
                #endregion

                #region -- SUT Counters ----------------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("SutCounters"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes != null &&
                            xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            DataSetCollectionForCharting.Add(AddChart(xChild, "SutCounters"));
                        }
                    }
                }
                #endregion

                #region -- Test Rig Counters -----------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("TestRigCounters"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            string alias;
                            if (xChild.Attributes["machineList"].Value.Contains("__"))
                                alias = aliases[xChild.Attributes["machineList"].Value];
                            else
                                alias = xChild.Attributes["machineList"].Value;
                            
                            DataSetCollectionForCharting.Add(AddChart(xChild, "KeyMetricsCounters"));
                        }
                    }
                }
                #endregion

                #region -- Combo Charts ----------------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("ComboChartSets"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            string chartName = xChild.Attributes["name"].Value;
                            foreach (XmlNode xChild2 in xChild.ChildNodes)
                            {
                                if (xChild2.Attributes["enabled"].Value.ToLower() == "true")
                                {
                                    //AddComboChartObjectsToTable(
                                    //    chartName,
                                    //    xChild2.Attributes["category"].Value,
                                    //    xChild2.Attributes["counter"].Value,
                                    //    xChild2.Attributes["instance"].Value,
                                    //    xChild2.Attributes["fixedscale"].Value,
                                    //    ChartCategory.ComboMetric);
                                }
                            }
                        }
                    }
                }
                #endregion

            }
            catch (XmlException ex)
            {
                errorString = ex.ToString();
            }
            sw.Stop();
            return sw.Elapsed;
        }
        #endregion

        #region -- Private Methods ------------------------------------------------------

        private TSL_ChartingDataSingleChart AddChart(XmlNode xNode, string chartCategory)
        {
            TSL_ChartingDataSingleChart _chart = new TSL_ChartingDataSingleChart();

            foreach (XmlNode xChild in xNode.ChildNodes)
            {
                if (xChild.Attributes != null &&
                    xChild.Attributes["enabled"].Value.ToLower() == "true")
                {
                    _chart.SingleChartDataCollection.Add(AddSeriesItem(xChild));
                }
            }

            return _chart;
        }

        private TSL_ChartingDataSeries AddSeriesItem(XmlNode xNode)
        {
            TSL_ChartingDataSeries _series = new TSL_ChartingDataSeries();
            _series.categoryName = (xNode.Attributes["category"] == null) ? "" : xNode.Attributes["category"].Value.Trim();
            _series.counterName = (xNode.Attributes["counter"] == null) ? "" : xNode.Attributes["counter"].Value.Trim();
            _series.instanceName = (xNode.Attributes["instance"] == null) ? "" : xNode.Attributes["instance"].Value.Trim();
            _series.machineName = (xNode.Attributes["machineName"] == null) ? "" : xNode.Attributes["machineName"].Value.Trim();
            _series.fixedScale = (xNode.Attributes["fixedscale"] == null) ? -1 : Convert.ToInt32(xNode.Attributes["fixedscale"].Value.ToString());
            _series.lineWidth = (xNode.Attributes["lineWidth"] == null) ? -1 : Convert.ToInt32(xNode.Attributes["lineWidth"].Value.ToString());

            if (xNode.Attributes["lineColor"] != null)
            {
                System.Drawing.Color c = (System.Drawing.Color)TypeDescriptor.GetConverter(typeof(System.Drawing.Color)).ConvertFromString(xNode.Attributes["lineColor"].Value);
            }

            if(xNode.Attributes["chartSeriesDataType"] == null)
            {
                _series.seriesDataType = chartSeriesDataType.Perfmon;
            }
            else
            {
                switch(xNode.Attributes["chartSeriesDataType"].Value.Trim())
                {
                    case "TestCase":
                        _series.seriesDataType = chartSeriesDataType.TestCase;
                        break;
                    case "TransactionTimer":
                        _series.seriesDataType = chartSeriesDataType.TransactionTimer;
                        break;
                    case "Page":
                        _series.seriesDataType = chartSeriesDataType.Page;
                        break;
                    case "Error":
                        _series.seriesDataType = chartSeriesDataType.Error;
                        break;
                    default:
                        _series.seriesDataType = chartSeriesDataType.Perfmon;
                        break;
                }
            }

            return _series;
        }
        #endregion
    }

    public class TSL_ChartingDataSingleChart
    {
//Size_X
//Size_Y
//DPI
//titleFont
//axisTitleFont
//axisLabelFont

        #region -- Public Properties ----------------------------------------------------
        public List<TSL_ChartingDataSeries> SingleChartDataCollection { get; set; }

        public List<string> machineList { get; set; }

        public string chartTemplate { get; set; }

        public string chartTitle { get; set; }

        public string y_axisLabel { get; set; }

        public string x_axisLabel { get; set; }

        // use -1 for dynamic scaling
        //public int fixedScale { get; set; }

        public int topQty { get; set; }

        public string sortOrder { get; set; }

        public string chartCategory { get; set; }
        #endregion

        #region -- Private Properties ---------------------------------------------------
        #endregion

        #region -- Constructors ---------------------------------------------------------
        public TSL_ChartingDataSingleChart() 
        {
            SingleChartDataCollection = new List<TSL_ChartingDataSeries>();
            machineList = new List<string>();
        }

        public TSL_ChartingDataSingleChart(string title, string y_axis, string x_axis, int top, string order)
        {
            SingleChartDataCollection = new List<TSL_ChartingDataSeries>();
            machineList = new List<string>();
            this.chartTitle = title;
            this.y_axisLabel = y_axis;
            this.x_axisLabel = x_axis;
            this.topQty = top;
            this.sortOrder = order;
        }

        #endregion

        #region -- Public Methods -------------------------------------------------------
        public void AddSeries(ref TSL_ChartingDataSeries series)
        {
            this.SingleChartDataCollection.Add(series);
        }
        #endregion

        #region -- Private Methods ------------------------------------------------------
        #endregion
    }

    public class TSL_ChartingDataSeries
    {
        #region -- Public Properties ----------------------------------------------------
        #region Counter Properties From Config File
        /// <summary>
        /// This is the name that matches the CounterSetItem name
        /// </summary>
        public string name { get; set; }

        /// <summary>
        /// The counter name in full, which includes the Machine, Counter, Category and Instance
        /// </summary>
        public string fullCounterName { get; set; }

        /// <summary>
        /// The CATEGORY element of the counter
        /// </summary>
        public string categoryName { get; set; }

        /// <summary>
        /// The COUNTER element of the counter
        /// </summary>
        public string counterName { get; set; }

        /// <summary>
        /// The INSTANCE element of the counter
        /// </summary>
        public string instanceName { get; set; }

        /// <summary>
        /// The Color to use for the line
        /// </summary>
        public System.Drawing.Color lineColor { get; set; }

        public int lineWidth { get; set; }

        public int fixedScale { get; set; }

        /// <summary>
        /// The value used to set the maximum scale for this counterset. The value is automatically computed. In future versions, you will be able to override it here.
        /// </summary>
        public int chartObjectScale { get; set; }

        public chartSeriesDataType seriesDataType { get; set; }
        #endregion

        #region Calculated/Passed-In Counter Properties
        /// <summary>
        /// The LoadTestRunId used for this counterset collection
        /// </summary>
        public int runId { get; set; }

        /// <summary>
        /// The instanceId for the values contained in the counterset
        /// </summary>
        public int instanceId { get; set; }

        /// <summary>
        /// The name of the machine that these counters represent. Use the controller machine's name if the counterset is not machine specific.
        /// </summary>
        public string machineName { get; set; }

        /// <summary>
        /// What interval is used for the data. This is important when doing multi-run comparisons
        /// </summary>
        public int intervalSpan { get; set; }
        #endregion

        #region Counter Values
        /// <summary>
        /// MINIMUM value for the counter
        /// </summary>
        public decimal minValue { get; set; }

        /// <summary>
        /// AVERAGE value for the counter
        /// </summary>
        public decimal averageValue { get; set; }

        /// <summary>
        /// MAXIMUM value for the counter
        /// </summary>
        public decimal maxValue { get; set; }

        /// <summary>
        /// Standard Deviation value for the counter
        /// </summary>
        public decimal stdDev { get; set; }

        /// <summary>
        /// Mean value for the counter
        /// </summary>
        public decimal meanValue { get; set; }

        /// <summary>
        /// 90 % value for the counter
        /// </summary>
        public decimal ninetyPct { get; set; }

        /// <summary>
        /// 95% value for the counter
        /// </summary>
        public decimal ninetyFivePct { get; set; }

        /// <summary>
        /// 5% value for the counter
        /// </summary>
        public decimal fivePct { get; set; }
        #endregion

        /// <summary>
        /// This is a list of the actual interval values that perfmon collected.
        /// </summary>
        public DataTable testIntervalValues { get; set; }
        #endregion

        #region -- Private Properties ---------------------------------------------------
        #endregion

        #region -- Constructors ---------------------------------------------------------
        public TSL_ChartingDataSeries()
        {
            //testIntervalValues = new DataTable();
            //scaledTestIntervalValues = new DataTable();
        }

        public TSL_ChartingDataSeries(string runId, int instanceId)
        {

        }
        #endregion

        #region -- Private Methods ------------------------------------------------------
        #endregion

        #region -- Public Methods -------------------------------------------------------
        /// <summary>
        /// Uses the testIntervalValues list and fills in the scaledTestIntervalValues by recomputing every value to a scale between 0 and 100.
        /// </summary>
        public void buildScaledValues()
        {

        }

        /// <summary>
        /// Gets a scale multiplier to use when building scaled charts. Pass in the maximum value from a series of data points and
        /// use the return value [scale] with the following formula to plot all the points on a scale of 0 to 100 ((DataPoint / [scale]) * 100)
        /// </summary>
        /// <param name="maxValue">the maxValue from a series of Data Points</param>
        /// <returns>a scale value</returns>
        public static double GetScale(double maxValue)
        {
            return Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(maxValue))) + 1);
        }

        /// <summary>
        /// Performs a "shallow" clone by using this.MemberwiseClone
        /// </summary>
        /// <returns></returns>
        public TSL_ChartingDataSeries ShallowCopy()
        {
            return (TSL_ChartingDataSeries)this.MemberwiseClone();
        }

        /// <summary>
        /// Gets the test intervals values from the DataTable and returns them as a List
        /// </summary>
        /// <returns></returns>
        public List<double> GetTestIntervalValuesForCharting()
        {
            List<double> _list = new List<double>();

            for (int x = 0; x < testIntervalValues.Rows.Count; x++)
            {
                _list.Add((System.Single)testIntervalValues.Rows[x][0]);
            }

            return _list;
        }

        /// <summary>
        /// Gets the test intervals keys from the DataTable and returns them as a List
        /// </summary>
        /// <returns></returns>
        public List<int> GetTestIntervalKeysForCharting()
        {
            List<int> _list = new List<int>();

            for (int x = 0; x < testIntervalValues.Rows.Count; x++)
            {
                _list.Add((int)testIntervalValues.Rows[x][1]);
            }

            return _list;
        }

        /// <summary>
        /// Stores the TimeSpan representing the time to execute the SQL query to collect the timing intervals.
        /// </summary>
        public TimeSpan timeToRetrieveIntervalValues { get; set; }
        #endregion

        #region not currently used
        /// <summary>
        /// The style (solid, dotted, etc.) to use for the line
        /// </summary>
        public string lineType { get; set; }

        /// <summary>
        /// What starting point is used for the data.
        /// </summary>
        public int startingOffset { get; set; }

        /// <summary>
        /// What ending point is used for the data.
        /// </summary>
        public int endingOffset { get; set; }

        /// <summary>
        /// The number of total points that could exist for this counterset (duration of run / intervalSpan)
        /// </summary>
        public int numberOfDataPointsUsed { get; set; }

        /// <summary>
        /// This is a list of the interval values that have been scaled to fit on a graph with other collections.
        /// </summary>
        public DataTable scaledTestIntervalValues { get; set; }
        #endregion
    }

    public enum chartSeriesDataType
    {
        TestCase = 1,

        TransactionTimer = 2,

        Page = 3,

        Error = 4,

        Perfmon = 5
    };
}


